package com.ruoyi.groovy.utils;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.stereotype.Component;
import org.springframework.util.ClassUtils;
import org.springframework.util.ReflectionUtils;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

/**
 * 业务聚合
 *
 * @version [V2.0]
 * @package com.ruoyi.groovy;
 * @description:[用一句话描述该文件做什么]
 * @author: [zhuben]
 * @date: [2023-08-21 16:35]
 * @copyright: 2023 echovin.com
 */
@Component(value = "Spring2Utils")
public class SpringUtils implements ApplicationContextAware {
    private static final String REQUEST_MAPPING_HANDLER_MAPPING = "requestMappingHandlerMapping";
    private static final String DETECT_HANDLER_METHODS = "detectHandlerMethods";
    private static final String GET_MAPPING_FOR_METHOD = "getMappingForMethod";
    private static ApplicationContext applicationContext;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        SpringUtils.applicationContext = applicationContext;
    }

    /**
     * 实例化bean
     * @param aClass
     */
    public static void createBean(Class aClass){
        //若存在先删除
        removeBean(aClass);

        ConfigurableApplicationContext configurableApplicationContext = (ConfigurableApplicationContext) applicationContext;
        char[] chars = aClass.getSimpleName().toCharArray();
        chars[0]+=32;
        BeanDefinitionRegistry beanFactory = (BeanDefinitionRegistry)configurableApplicationContext.getBeanFactory();
        AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition(aClass).getBeanDefinition();
        beanFactory.registerBeanDefinition(String.valueOf(chars), beanDefinition);
    }

    /**
     * 删除bean
     * @param aClass
     */
    public static void removeBean(Class aClass){
        ConfigurableApplicationContext configurableApplicationContext = (ConfigurableApplicationContext) applicationContext;
        char[] chars = aClass.getSimpleName().toCharArray();
        chars[0]+=32;
        BeanDefinitionRegistry beanFactory = (BeanDefinitionRegistry)configurableApplicationContext.getBeanFactory();
        boolean beanNameInUse = beanFactory.isBeanNameInUse(String.valueOf(chars));
        if (beanNameInUse){
            beanFactory.removeBeanDefinition(String.valueOf(chars));
        }
    }

    /**
     * 注册requestMapping
     * @param aClass
     * @throws NoSuchMethodException
     * @throws InvocationTargetException
     * @throws IllegalAccessException
     */
    public static void registRequestMapping(Class aClass) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        final RequestMappingHandlerMapping requestMappingHandlerMapping = (RequestMappingHandlerMapping)
                applicationContext.getBean(REQUEST_MAPPING_HANDLER_MAPPING);
        //若已存在则删除，否则无法再次注册
        unregistRequestMapping(aClass);
        //实例化该bean
        createBean(aClass);
        Method method = requestMappingHandlerMapping.getClass().getSuperclass().getSuperclass().
                getDeclaredMethod(DETECT_HANDLER_METHODS, Object.class);
        method.setAccessible(true);
        char[] chars = aClass.getSimpleName().toCharArray();
        chars[0]+=32;
        method.invoke(requestMappingHandlerMapping, String.valueOf(chars));

    }

    /**
     * 移除存在的requestMapping
     * @param targetClass
     */
    public static void unregistRequestMapping(Class targetClass) {
        final RequestMappingHandlerMapping requestMappingHandlerMapping = (RequestMappingHandlerMapping)
                applicationContext.getBean(REQUEST_MAPPING_HANDLER_MAPPING);
        //移除已实例化的bean
        removeBean(targetClass);
        //移除requestMapping
        ReflectionUtils.doWithMethods(targetClass, method -> {
            Method specificMethod = ClassUtils.getMostSpecificMethod(method, targetClass);
            try {
                Method createMappingMethod = RequestMappingHandlerMapping.class.
                        getDeclaredMethod(GET_MAPPING_FOR_METHOD, Method.class, Class.class);
                createMappingMethod.setAccessible(true);
                RequestMappingInfo requestMappingInfo = (RequestMappingInfo)
                        createMappingMethod.invoke(requestMappingHandlerMapping, specificMethod, targetClass);
                if (requestMappingInfo != null) {
                    requestMappingHandlerMapping.unregisterMapping(requestMappingInfo);
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }, ReflectionUtils.USER_DECLARED_METHODS);
    }
}
